import { defineStore } from 'pinia'
import { reactive } from "vue";

export type IPrinter = {
  name: string;
}


export type PrinterCommand<T = any> = {
  cmd: string;
  requestID: string;
  version: string;
} & T

export type PrintTask = any

export type PrintTaskReq = PrinterCommand<PrintTask>

export type PrintTaskResp = PrinterCommand<{
  taskID: string;
  status: string;
}>

export type GetPrinterResp = PrinterCommand<{
  defaultPrinter?: string;
  printers: IPrinter[]
}>

export type NotifyPrintResult = PrinterCommand<{
  printStatus: { 
    detail: string; 
    msg: string; 
  }[];
  printer: string;
  status: string;
  taskID: string;
  taskStatus: string;
}>


export type SendCommand<T = any> = (cmd: string, data?: any, timeout?: number) => Promise<PrinterCommand<T>>

export type PrinterServerState = {
  status: 'connecting' | 'connected' | 'closed';
  _lock: boolean;
  _resolve?: (value: PrinterCommand<any>) => void;
  _reject?: (err: any) => void;
  _cmdTimer?: any;
  _wsCheckTimer?: any;
  _request?: PrinterCommand<any>;
}

export class PrinterError<T = unknown> extends Error {
  readonly request?: PrinterCommand<T>

  constructor(message: string, request?: PrinterCommand<T>) {
    super(message);
    this.request = request
  }
}

export const usePrinterStore = defineStore('printer', () => {
  
  // 服务状态
  const state = reactive<PrinterServerState>({
    status: 'closed',
    _lock: false
  })

  // 重置命令
  const resetCmd = () => {
    state._lock = false
    state._resolve = undefined
    state._reject = undefined
    state._request = undefined
    if (state._cmdTimer) clearTimeout(state._cmdTimer)
  }

  let server: WebSocket | undefined;

  // 启动 websocket 服务
  const start = (reconn = true) => {
    server = new WebSocket('ws://localhost:16888/ks/printer')

    state.status = 'connecting'

    server.addEventListener('error', (ev) => {
      if (!import.meta.env.PROD) console.warn('printer ws error: ', ev)
      if (state._reject && state._request) {
        state._reject(new PrinterError('连接异常', state._request))
        resetCmd()
      }
    })

    server.addEventListener('open', () => {
      state.status = 'connected'
    })

    server.addEventListener('close', () => {
      state.status = 'closed'
      if (state._request && state._reject) {
        state._reject(new PrinterError('连接已关闭, 命令终止', state._request))
      }
      resetCmd()
    })

    server.addEventListener('message', (evt: MessageEvent<string>) => {
      const data: PrinterCommand<unknown> = JSON.parse(evt.data)
      if (state._request?.cmd === data.cmd && state._request?.requestID === data.requestID && state._resolve) {
        state._resolve(data)
        resetCmd()
      }
    })

    if (reconn) {
      state._wsCheckTimer = setInterval(() => {
        if (server && server.readyState === WebSocket.CLOSED) {
          start()
        }
      }, 5000)
    }
  }
  const stop = () => {
    console.log('closing printer websocket')
    server?.close()
    server = undefined
  }

  // 发送命令
  function send<T = any>(cmd: string, data?: any, timeout = 1000) {
    const req: PrinterCommand = {
      cmd,
      requestID: new Date().getTime() + '',
      version: '1.0',
      ...data
    }
    return new Promise<PrinterCommand<T>>((resolve, reject) => {
      if (!server || server.readyState !== WebSocket.OPEN) {
        reject(new PrinterError('服务连接异常', req))
        return
      }
      if (state._lock) {
        reject(new PrinterError('正在请求其他命令, 暂时无法操作', req))
        return
      }
      state._resolve = resolve
      state._reject = reject
      state._request = req
      state._lock = true
      state._cmdTimer = setTimeout(() => {
        reject(new PrinterError('命令响应超时', req))
        resetCmd()
      }, timeout)
      server.send(JSON.stringify(req))
    })
  }

  const getPrinters = () => send<GetPrinterResp>('getPrinters')
  const printTask = async (task: PrintTask) => {
    return new Promise<PrintTaskResp>(async (resolve, reject) => {
      const res = await send<PrintTaskResp>('print', { task })
      if (res.status !== 'success') {
        reject(new PrinterError(res.status))
      } else {
        resolve(res)
      }
    })
  }
  const waitTask = (requestID: string, taskID: string) => {
    const req: PrinterCommand = {
      cmd: 'notifyPrintResult',
      requestID,
      taskID,
    }
    return new Promise<NotifyPrintResult>((resolve, reject) => {
      if (!server || server.readyState !== WebSocket.OPEN) {
        reject(new PrinterError('服务连接异常', req))
        return
      }
      if (state._lock) {
        reject(new PrinterError('正在请求其他命令, 暂时无法操作', req))
        return
      }
      state._resolve = resolve
      state._reject = reject
      state._request = req
      state._lock = true
      state._cmdTimer = setTimeout(() => {
        reject(new PrinterError('命令响应超时', req))
        resetCmd()
      }, 10000)
    }).then(res => {
      if (res.status !== 'success') {
        throw new PrinterError(res.printStatus.map(i => i.detail || i.msg).join('\n'))
      } else {
        return res
      }
    })
  }

  return {
    state,
    send,
    getPrinters,
    start,
    stop,
    printTask,
    waitTask
  }
})